﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using CredentialsManager.Common;
using CredentialsManager.Helpers;
using CredentialsManager.Properties;
using MVVMFoundation;

namespace CredentialsManager.ViewModels
{
    public class RoleListViewModel : ViewModelBase
    {
        #region Private classes

        private class DeleteWorkerInput
        {
            public string Application { get; set; }
            public IList<string> RolesToDelete { get; set; }
        }

        #endregion

        #region Private member variables

        private string m_Filter;
        private ObservableCollection<string> m_AllRoles;
        private ReadOnlyCollection<string> m_Roles;
        private ObservableCollection<string> m_SelectedRoles;
        private RelayCommand m_CreateRoleCommand;
        private RelayCommand m_DeleteRoleCommand;
        private BackgroundWorker m_DeleteWorker;

        #endregion

        #region Constructor

        public RoleListViewModel(string application)
        {
            this.Application = application;
            this.SelectedRoles = new ObservableCollection<string>();
            this.ProgressMessage = string.Empty;

            var proxy = ProxyFactory.Create<RoleManagerClient, IRoleManager>(ServiceAddressList.CredentialsService);

            try
            {
                m_AllRoles = proxy.GetAllRoles(application);
                FilterRoles();
            }
            finally
            {
                ProxyFactory.Cleanup<RoleManagerClient, IRoleManager>(proxy);
            }

            m_DeleteWorker = new BackgroundWorker();
            m_DeleteWorker.DoWork += m_DeleteWorker_DoWork;
            m_DeleteWorker.ProgressChanged += m_DeleteWorker_ProgressChanged;
            m_DeleteWorker.RunWorkerCompleted += m_DeleteWorker_RunWorkerCompleted;
        }

        #endregion

        #region Public properties

        public string Application { get; private set; }

        public ReadOnlyCollection<string> Roles
        {
            get
            {
                return m_Roles;
            }
            private set
            {
                m_Roles = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        public ObservableCollection<string> SelectedRoles
        {
            get
            {
                return m_SelectedRoles;
            }
            set
            {
                m_SelectedRoles = value;

                m_SelectedRoles.CollectionChanged += delegate(
                    object sender, NotifyCollectionChangedEventArgs e)
                {
                    Mediator.NotifyColleagues<object>(MediatorMessages.SelectedRoleChanged, null);
                };                

                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        private void m_SelectedRoles_CollectionChanged(
            object sender, NotifyCollectionChangedEventArgs e)
        {
            throw new NotImplementedException();
        }

        public string Filter
        {
            get
            {
                return m_Filter;
            }
            set
            {
                m_Filter = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                FilterRoles();
            }
        }

        #endregion

        #region Background worker handlers

        private void m_DeleteWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var input = e.Argument as DeleteWorkerInput;
            Debug.Assert(input != null && input.RolesToDelete != null
                && input.RolesToDelete.Count > 0);

            var backgroundWorker = sender as BackgroundWorker;
            Debug.Assert(backgroundWorker != null);

            var proxy = ProxyFactory.Create<RoleManagerClient, IRoleManager>(ServiceAddressList.CredentialsService);

            try
            {
                DeleteRolesWithProgress(input, backgroundWorker, proxy);
                e.Result = proxy.GetAllRoles(input.Application);
            }
            finally
            {
                ProxyFactory.Cleanup<RoleManagerClient, IRoleManager>(proxy);
            }
        }

        private void m_DeleteWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.ProgressMessage = string.Format(Resources.DeletingRolesProgressMessage.ToString(),
                e.ProgressPercentage.ToString());
            Mediator.NotifyColleagues<string>(MediatorMessages.ProgressMessageChanged, this.ProgressMessage);
        }

        private void m_DeleteWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                ErrorHandler.HandleNonSpecificWCFException(e.Error, this.DisplayName);
            }
            else
            {
                m_AllRoles = e.Result as ObservableCollection<string>;
                Debug.Assert(m_AllRoles != null);
                FilterRoles();
                Mediator.NotifyColleagues<object>(MediatorMessages.SelectedRoleChanged, null);
                Mediator.NotifyColleagues<object>(MediatorMessages.SelectedUserChanged, null);
            }

            this.ProgressMessage = string.Empty;
            this.IsLoading = false;
        }

        #endregion

        #region Private methods

        private void DeleteRolesWithProgress(DeleteWorkerInput input,
            BackgroundWorker backgroundWorker, RoleManagerClient proxy)
        {
            int lastProgress = 0;

            for (int index = 0; index < input.RolesToDelete.Count; index++)
            {
                proxy.DeleteRole(input.Application, input.RolesToDelete[index], false);
                int progress = (index / input.RolesToDelete.Count) * 100;

                if (progress > lastProgress)
                {
                    lastProgress = progress;
                    backgroundWorker.ReportProgress(progress);
                }
            }
        }

        private void FilterRoles()
        {
            if (string.IsNullOrEmpty(this.Filter) || this.Filter.Trim() == string.Empty)
            {
                this.Roles = new ReadOnlyCollection<string>(m_AllRoles);
            }
            else
            {
                var roles = (from r in m_AllRoles
                             where r.ToUpper().Contains(this.Filter.ToUpper())
                             select r).ToList<string>();

                this.Roles = new ReadOnlyCollection<string>(roles);
            }
        }

        private void CreateRole()
        {
            var createRoleUserDialog = base.GetService<IDialogService>();
            Debug.Assert(createRoleUserDialog != null);

            var roleViewModel = new CreateRoleViewModel(this.Application);
            createRoleUserDialog.ShowModal(roleViewModel);

            var newRoles = from nr in roleViewModel.NewRoles
                           join r in m_AllRoles
                           on nr equals r into j
                           from r in j.DefaultIfEmpty()
                           where r == null
                           select nr;

            foreach (var newRole in newRoles)
            {
                m_AllRoles.Add(newRole);
            }

            FilterRoles();
        }

        private void DeleteRole()
        {
            Debug.Assert(!this.IsLoading && !m_DeleteWorker.IsBusy);

            if (this.SelectedRoles != null && this.SelectedRoles.Count > 0)
            {
                IMessageBoxService messageBox = base.GetService<IMessageBoxService>();
                Debug.Assert(messageBox != null);

                string message = Resources.ConfirmRolesDeletionMessage;

                if (m_SelectedRoles.Count == 1)
                {
                    message = Resources.ConfirmRoleDeletionMessage;
                }

                MessageBoxResult result = messageBox.Show(message, this.DisplayName,
                    MessageBoxButton.YesNo, MessageBoxImage.Question);

                if (result == MessageBoxResult.Yes)
                {
                    this.IsLoading = true;
                    var workerInput = new DeleteWorkerInput()
                    {
                        Application = this.Application,
                        RolesToDelete = this.SelectedRoles
                    };

                    m_DeleteWorker.RunWorkerAsync(workerInput);
                }
            }
        }

        #endregion

        #region Commands

        public ICommand CreateRoleCommand
        {
            get
            {
                return m_CreateRoleCommand ?? (m_CreateRoleCommand = new RelayCommand(
                    param => this.CreateRole()));
            }
        }

        public ICommand DeleteRoleCommand
        {
            get
            {
                return m_DeleteRoleCommand ?? (m_DeleteRoleCommand = new RelayCommand(
                    param => this.DeleteRole()));
            }
        }

        #endregion
    }
}
